home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Light ROM 3
/
Light ROM 3 - Disc 2.iso
/
programs
/
amiga
/
imagefx
/
imagekit
/
imagekit.lha
/
ImageFX_SDK
/
doc
/
Modules.doc
< prev
next >
Wrap
Text File
|
1992-12-16
|
28KB
|
673 lines
EXPANDING IMAGEFX
Developer Documentation
for the ImageFX Image Processing System
by Thomas Krehbiel
WARNING: This document assumes that you are familiar with programming on the
Amiga, especially in the 'C' language. It also assumes that you are at least
somewhat familiar with the ImageFX program itself.
0. CONTENTS
0. Contents
1. Introduction
2. Basics
2.1. Compiling Conventions
2.2. ScanBase
3. Modules
3.1. Theory
3.2. Standard Module Startup Code
3.3. Module Caveats
3.4. Types of Modules
3.2.1. Loader
3.2.2. Saver
3.2.3. Render
3.2.4. Scanner
3.2.5. Printer
3.2.6. Preview
3.5. Language
3.6. Preferences
3.7. Modules and Arexx
4. Hooks
4.1. Theory
4.2. Hook Construction
4.3. Language
4.4. Hooks and Arexx
1. INTRODUCTION
ImageFX was designed to be expandable by anyone with an inclination
towards programming, through the use of Modules and Hooks. What are Modules
and Hooks you ask? Well, sit back and I'll explain:
A "Module" is like an Amiga shared library. ImageFX loads the module into
memory and calls functions in it to get things done. Every effort was made to
make "black boxes" of these modules, thus it is possible to update or even
replace modules without bothering ImageFX. Modules are broken down into
several types, each one designed to do a specific thing. They are: Loader,
Saver, Preview, Scanner, Render, Printer, Quantize, and Dither modules. Each
module type is explained in more detail below.
A "Hook" is a more free-form type of expansion for ImageFX. It is
essentially just a program that is able to access much of the internal "stuff"
of ImageFX, including image buffers and the like. ImageFX launches the Hook,
waits for it to complete, and then continues as normal. Thus a Hook has
virtually unlimited application, ranging from adding simple effects to adding
whole new interfaces to the program.
2. BASICS
This section describes some things you should know that are common to both
the Modules and Hooks.
2.1. Compiling Conventions
This document assumes you are using the SAS/C compiler. All examples and
code were compiled with SAS/C 5.10b, and all object modules provided are in the
standard Amiga link library format. They may require some modification to
compile under other systems (especially Aztec).
32-bit int's are assumed throughout this document.
All example source code assumes you are using at least the V37 include
files. If you don't have them, I
strongly
suggest you get them along with the
2.0 enhancer kit. The sooner we stamp out 1.3, the better off we'll all be...
:)
2.2. ScanBase
When ImageFX is loaded, it creates a shared library in memory. This
library contains a plethora of useful functions for dealing with ImageFX. The
library structure (struct ScanBase) itself also contains a lot of useful
information, like Screen pointers, drawing mode state information, and things
like that. For details on the functions, consult the appropriate autodoc file.
Since more than one ImageFX may be running at once, the name of the
library has to also be different for each copy of the program. The convention
used is as follows: For the first copy of ImageFX loaded, the library is
called "scan1.library"; for the second copy of ImageFX, the library is called
"scan2.library"; and so on.
Normally, you will not need to worry about opening the library yourself.
Modules are passed a pointer to ScanBase which they can use directly. Standard
initialization code provided for writing Hooks also takes care of getting the
right ScanBase pointer. You generally only need to know that there is a global
ScanBase pointer available, and thus all the functions of that library are
available.
The library function calls are all registerized, so it should be possible
to call scan.library from just about any language. The developer distribution
uses pragmas for the library calls, so you may need to adjust this for your
particular setup. An FD file is included so you can build your own stub
library if necessary.
A link-time library ("scan.lib") is included which contains varargs
versions of some of the functions in scan.library, as well as few other handy
functions not included in scan.library.
3. MODULES
3.1. Theory
ImageFX Modules are strongly based on Amiga shared libraries. They are
built in almost exactly the same way. If you aren't familiar with how to
create a shared library, I strongly suggest that you review the subject (check
the RKMs) before delving into this.
A Module has the 4 standard library vectors of OPEN, CLOSE, EXPUNGE, and
RESERVED. In addition, each type of module has additional vectors which must
be provided (see individual descriptions below).
Each Module has a ModuleBase structure associated with it (see
"scan/mod.h"), which is based on the standard Exec Library structure. The
ModuleBase structure is allocated by ImageFX and passed to the Module's OPEN
vector, where the Module's job is to fill in the fields of the structure and
return it. This is also where the module does any pre-use initialization
required (such as opening libraries, etc.). By "pre-use" I mean any
initialization that will cause the Module open to fail if something goes wrong
(for example, if a module depends on some kind of hardware and the hardware is
not found). For most modules there is another function to do less critical
initializations such as initializing variables and such.
ImageFX will fill in a few fields in the ModuleBase structure before
passing it to the Module, most notably the SysLib and ScanBase pointers. These
may be copied to the Module's global SysBase and ScanBase pointers
respectively, to give access to those libraries. ScanBase also contains
pointers to IntuitionBase and GfxBase, so those may also be copied if needed.
Some of the common fields of ModuleBase that the Module OPEN vector should
fill in are described below:
NewGad
: Points to an array of NewGad structures, which describes the GUI
(gadgets, text, etc.) for this module (if it has one).
Language
: A string representing the "tag" to look for to find all the
text associated with this module. A NULL indicates this module has no text, or
you're not bothering with internationalizing the module. See the section below
on internationalizing ImageFX modules.
LangCount
: The total number of text strings to look for.
CmdTable
: Pointer to an array of RXCMD structures, which describes the
Arexx commands that this module understands. Loaders and Savers only use the
first entry. If NULL, this module has no Arexx commands.
PrefID
: Four-byte ID "tag" used to locate and store preferences settings
for this module. Each module MUST have a unique 4-byte ID, or disaster could
result (or at least mixed-up preferences settings). If 0, this module has no
preferences.
PrefLen
: Length of the structure that contains the module's preferences
settings. ImageFX will ask the module to fill in a structure of this size,
which it will then write to disk when the user wants to Save Prefs. Similarly,
when the user Loads Prefs, a structure of this size which has been read from
disk will be presented to the module.
NewWin
: Pointer to a NewWindow structure. Only used for Preview modules
so that ImageFX can open the Preview Options window. May be NULL if there are
no preview options.
All other fields of the ModuleBase structure should be considered
read
only. Tampering with them may be dangerous.
3.2. Standard Module Startup Code
I have provided a link-time module which does 99% of the grunt work of
creating a module. You need only provide a function table, the name and type
of your module, and special initialization and cleanup functions. The library
does the rest.
This wonderous achievement is called "lib.o", and should be the first
module in your link list.
Here are the things you need to specify:
LibraryID
: A pointer to a string which gives pertinant information about
your module. It should be in the form of a standard 2.0 version string:
"$VER: name version.revision (dd.mm.yy)"
LibraryType
: A byte indicating the type of module this is. Should be one
of the NT_* constants defined in "scan/mod.h".
FuncTable
: A LONG array, where each element is a pointer to a function.
This is the library vector table, and the first four functions should always be
LibOpen, LibClose, LibExpunge, and LibNull (they are defined in lib.o). The
remaining functions depend on the type of module (see below). The function
table must be terminated with a -1 entry.
UserOpen
: Initialization function called within the OPEN vector. This is
where you should take the time to initialize the appropriate fields of the
ModuleBase structure, which is passed to you in A6 (a good place to use SAS's
__asm register parameters). The ModuleBase parameter is also placed on the
stack.
UserClose
: Cleanup function called within the CLOSE vector. This should
cleanup anything that you (and only you) allocated in the UserOpen() function
above. This is also passed a pointer to struct ModuleBase in A6 and on the
stack.
The startup code will fill in the SysBase, ScanBase, IntuitionBase,
GfxBase, and ModuleBase (pointing to the module's own ModuleBase structure)
global pointers.
3.3. Module Caveats
Generally the same things that are not possible in an Amiga shared library
are not possible in a Module. Most notably, you cannot use any of the stdio
functions. Fortunately, however, there are similar buffered file I/O routines
provided in ScanBase (see Scan.autodoc for details).
While it is possible to use floating point math, you can only use the
inline FPU math (-f8/lcm881) or the SAS floating point (-fl/lcm) routines. You
may NOT use either of the Amiga floating point libraries (-fi/lcmieee or
-ff/lcmffp) for reasons better left unsaid. For obvious reasons, I'd recommend
staying away from the floating point altogether unless it's absolutely
necessary (most of the time it isn't).
It is generally a good idea to compile without stack depth checking (-v in
SAS) on.
If your module uses the small data model, you should make sure that any
functions in your module that get called by ImageFX (directly or indirectly)
load the A4 register to point to your data base. With SAS/C, you can use
__saveds for all such functions or take the lazy way out (like me :) and just
compile with the -y flag. I always do it the latter way so I'm not entirely
sure the former way will work -- it should though. If you're
really
lazy you
can just compile with -b0 and use the large data model.
Modules always run in the ImageFX task context, using ImageFX's stack.
Modules do not have to be reentrant.
3.4. Types of Modules
Well, on to the nitty-gritty details of the modules.
3.4.1. Loader Modules
Loader modules convert image files on disk into 8- or 24-bit buffers
suitable for use in ImageFX. They are also used for extracting palette
information from files (assuming the file format in question supports a
palette).
Generally speaking, each Loader module will handle one file format.
However, it is possible to allow a single Loader to be able to read one of
several file formats, or more commonly several variations of one file format.
This is done by passing back an array of structures indicating the types of
files the loader can read. Each file format must have its own unique ID number
associated with it; this ID value is passed to the module's load vector so it
knows which format was matched.
ImageFX was designed to automatically detect the file format of the image
the user is attempting to load, so that he doesn't have to know what it is
beforehand. It was also designed to retain this ability no matter how many
loader modules were added to the system. In order for ImageFX to know what to
look for, it scans the modules/loaders/ directory (at startup time) and calls
each loader module in turn. The loader module reports what "signature" bytes
that a file must have in order to be considered as the file format that this
loader expects. ImageFX takes this information and stores it in a list and
continues on to the next loader until all of them have been queried.
When the user asks to load a file, ImageFX reads the first few bytes of
the file and compares those bytes to all the entries in its list. If it
matches one of the entries, the approprate Loader module is loaded and control
passes to it to read the image into memory.
Another method of file format detection exists. If a file format does not
have any "magic" constants at the beginning of the file, a Loader module may
elect to do "custom file identification." This means that ImageFX will call
your Loader with the name of the file in question. You loader module may then
do whatever is necessary to determine whether this file is in the format
expected by this module. If it is not, ImageFX carries the search on to other
loader modules. If it is, control passes to your loader module to read the
file into memory. A Loader module indicates that it wants to do custom file
identification by returning NULL in the function that would normally indicate
what signature bytes to look for.
ImageFX also provides a method of automatically using a 68000 or 68030
version of a loader module. The 68000 version of the loader module should have
a file extension of ".000" and the 68030 version of the loader module should
have a file extension of ".030". ImageFX will determine which loader module to
use based on the CPU in the current system. Loader modules without any
extensions can be used on any system.
There are four functions that you need to provide in your Loader module.
They (in order) are LM_Load(), LM_LoadPalette(), LM_Signatures(), and
LM_CheckFile(). For detailed information about these function, consult the
loader module autodoc and the sample loader skeleton source code.
3.4.2. Saver Modules
Saver modules are responsible for storing 8- or 24-bit image buffers onto
disk in a particular file format. They also handle saving rendered (color
mapped) images.
Generally speaking, each Saver module will handle one file format.
However, it is possible to allow a single Saver to be able to write one of
several file formats, or more commonly several variations of one file format.
This is done by passing back an array of structures indicating the types of
files the saver can write. Each file format must have its own unique ID number
associated with it; this ID value is passed to the module's save vectors so it
knows which format was matched.
Similar to Loader modules, ImageFX will scan the entire
modules/savers/directory at startup time to record the names of all available
save formats.
When the user chooses to save a file, he is presented with a list of all
available file formats. After the user selects one, ImageFX decides on the
appropriate Saver module to call, and calls one of its save vectors to write
the file to disk (which vector depends on whether the user is saving a 24-bit
file or a rendered image).
Like Loader modules, ImageFX also provides a method of automatically using
a 68000 or 68030 version of a saver module. The 68000 version of the saver
module should have a file extension of ".000" and the 68030 version of the
saver module should have a file extension of ".030". ImageFX will determine
which saver module to use based on the CPU in the current system. Saver
modules without any extensions can be used on any system.
There are four functions that you need to provide in your Saver module.
They (in order) are SM_SaveTrue(), SM_SaveMapped(), SM_SavePalette(), and
SM_Signatures(). For detailed information about these functions, consult the
saver module autodoc and the source code for the saver module skeleton.
3.4.3. Render Modules
Render modules are responsible for displaying 24-bit image data on various
display devices. Generally this process involves quantizing (reducing the
colors in) a 24-bit buffer down to some fewer number of colors (16 or 256, for
example). Render modules are not interactive, that is, they simple the display
the image data. The user may choose to save the rendered image from the Save
gadget on the main menu.
Render modules are somewhat more complex than either Loader or Saver
modules because of the introduction of a GUI. The user must be able to have
some gadgets to click on when he chooses your render module, even if it is
nothing more than a "GO" gadget. Your render module should also follow the
convention of having a button in the upper left corner for changing the current
module.
ImageFX uses a somewhat unorthadox method of creating GUIs, which is
something that you'll have to learn to create Render (as well as Scanner,
Print, and Preview) modules. The system is loosely based on the 2.0
gadTools.library, but goes about things in a different way. Basically, you
hand ImageFX an array of structures describing the GUI; each element of the
array describes a gadget, bevel box, image, or bit of text. The gadgets in the
list may be given functions to call when they are "fiddled" with (buttons
pressed, sliders slid, etc.). ImageFX handles all of the event processing for
you. For further details of ImageFX's GUI system, refer to the Ged autodoc and
"scan/ged.h".
Render modules are loaded only when they are first accessed by the user
(when he first clicks on the Render gadget). The module segment is then loaded
into memory, and the standard module initialization is performed. Render
modules also have additional initialization steps to go through (the functions
RM_Init
and
RM_Show
).
<more to come - see example skeleton render module>
3.4.5. Printer Modules
<more to come>
3.4.6. Preview Modules
<more to come - see example skeleton preview module>
3.5. Language
ImageFX was designed as an internationally-aware program. All the text
used in the main program may be translated by the use of an external text file.
If this text file exists, ImageFX reads the text into an array in memory, and
all text references within the program are made as indexes into this array.
Otherwise, internal (English) defaults are used. This allows for easy
translation of the program into other languages.
ImageFX also provides a way for modules to use the same technique of
storing text in an external text file. Modules should be designed to take
advantage of this, so that they may be translated to other languages as well
(remember that the majority of Amigas are in Europe).
If you want your module to be internationally-aware, there are a couple
things you need to do:
In your module's Open() vector, you should fill in the "Language" and
"LangCount" fields of the ModuleBase structure. ImageFX will then try to
locate the text of your module in a text file based on your language tag and
return a pointer to the resulting array of text strings in ModuleBase->Text.
If the language text is found, you should get your text strings from this array
only.
The standard module startup code contains a helpful function for use in
internationalizing your modules: GetStr(). You should use this function for
all your text references. The format of the function call is as follows:
char *text = GetStr (int index, char *default_text)
This function will examine your ModuleBase->Text field to see if an
external text file was successfully read. If so, it will return the text at
the given index to you. If the text was not read in, then the default text
string will be returned.
The name of the external text file depends on the type of module you are
creating and the language tag you specify in ModuleBase->Language. ImageFX
will use a template like this: "%ls_%ls.text", where the first argument is the
*type* of module (eg. Loader, Render, Saver, etc.) and the second argument is
the language tag you specify. For example, a scanner module with the language
tag "Fuji" would reference the text file "Scanner_Fuji.text".
A quick, fragmented example of a render module follows:
/*******************************************************
* In the module, you should define your text as indexes
* into an array instead of as strings. This is the
* index array:
*/
enum {
TXT_Hello,
TXT_Goodbye,
TXT_Yikes,
TXT_COUNT /* 3 entries */
};
/*******************************************************
* In the module Open() vector, setup the ModuleBase
* Language and LangCount fields:
*/
ModuleBase->Language = "MyModuleText";
ModuleBase->LangCount = TXT_COUNT;
/*******************************************************
* Now whenever you want to use text, you reference it using
* the GetStr() function.
*/
Errorf(GetStr(TXT_Yikes, "Yikes!"));
;********************************************************
;* And in the ImageFX Text/ directory, you create a file
;* called "Render_MyModuleText.text" with the following lines:
;*
!MyModuleText
Hello
Goodbye
Yikes - an error!
#
3.6. Preferences
<more to come>
3.7. Modules and Arexx
<more to come>
4. HOOKS
4.1. Theory
ImageFX Hooks are nothing more than executable programs. When ImageFX
launches a hook, it passes the name of the ImageFX function library to it as
the first argument on the command line. Once the hook program has opened this
library, it can access all of the functions in ImageFX, allowing it to get to
the image buffers, or whatever. Additional command line arguments may be
passed if the hook invocation came from Arexx.
4.2. Hook Construction
A link-time startup module is provided to make building your hooks easier.
It takes care of all the "grunt" work of opening the function library and
making sure everything is setup properly. In addition, it copies certain
library pointers (IntuitionBase and GfxBase) into global variables for your
immediate use.
The hook startup code calls the function "hook_main()" to invoke your hook
code. This is the only function you need to provide.
There are also three global variables you need to set so that the hook
startup code knows what to do. They are described below:
HookName
: (char *) The name of your hook program.
HookText
: (char *) Language tag to search for for this hook program (see
"Language" below).
HookTextCount
: (int) Number of text entries to read for this hook program
(see "Language" below).
Failure to set these will probably result in a link error.
4.3. Language
Hook programs may also be designed to take advantage of ImageFX's
internationality.
The standard hook startup code contains a helpful function for use in
internationalizing your modules: GetStr(). You should use this function for
all your text references. The format of the function call is as follows:
char *text = GetStr (int index, char *default_text)
This function will check to see if an external text file was successfully
read. If so, it will return to you the text at the given index. If the text
was not read in, then the default text string will be returned.
A quick, fragmented example follows:
/*******************************************************
* In the hook, you should define your text as indexes
* into an array instead of as strings. This is the
* index array:
*/
enum {
TXT_Hello,
TXT_Goodbye,
TXT_Yikes,
TXT_COUNT /* 3 entries */
};
/*******************************************************
* Use the following globals to tell the hook startup
* code what to look for:
*/
HookText = "MyHookText";
HookTextCount = TXT_COUNT;
/*******************************************************
* Now whenever you want to use text, you reference it using
* the GetStr() function.
*/
Errorf(GetStr(TXT_Yikes, "Yikes!"));
;********************************************************
;* And in the ImageFX Text/ directory, you create a file
;* called "Hook_MyHookText.text" with the following lines:
;*
!MyHookText
Hello
Goodbye
Yikes - an error!
#
4.4. Hooks and Arexx
When a hook is invoked via. Arexx, arguments may be passed to your hook
code via. the command line.
The hook_main() function has two arguments just like a standard main()
function; 'argc' and 'argv'. You can use these to peruse any arguments that
were sent to your hook program via. Arexx.
For example, suppose the user issued the command:
"Hook YourHook 1 4 Cookies"
Your hook_main() function will get the following:
argc = 4
argv[0] = <ignored>
argv[1] = "1"
argv[2] = "4"
argv[3] = "Cookies"
Why is the first one ignored? To keep the same conventions as standard C
main() functions.
If you need to get to the RexxMsg that invoked your hook program (for
example, to setup result strings or set variables), you can look in the field
ScanBase->sb_HookMsg. If this field is NULL, then your hook was not invoked by
Arexx (ie. the user could have just typed something in the command shell).
Otherwise, it points to a RexxMsg structure which you can use for whatever
purpose.
A handy function contained in ScanBase is called SetResult(). It lets you
set the result string to return to an Arexx program in a fairly easy fashion.
It's prototype is:
BOOL SetResult (struct RexxMsg *msg, char *fmt, ...);
The result string to pass back to the Arexx program associated with the
given RexxMsg will be set to the formatted string passed in. It is safe to
pass a NULL for the RexxMsg, in which case nothing is done.